<?php
// +----------------------------------------------------------------------
// | sfadmin - Suifeng Administration
// +----------------------------------------------------------------------
// | Copyright (c) 2017-2020 http://suifeng.tech All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: suifeng <1787585212@qq.com>
// +----------------------------------------------------------------------

namespace app\admin\service;

use suifeng\mvc\ModuleHelper;

class Menu extends \suifeng\mvc\Service
{
    /**
     * 返回后台管理菜单。
     */
    public function get_admin_menu()
    {
        $admin_menu = cache('admin_menu_service::admin_menu');

        if (!$admin_menu || APP_DEBUG) {
            $admin_menu = new MenuItem(MenuItem::ROOT_NAME);
            ModuleHelper::invokeMethod(null, 'admin_menu', $admin_menu);
            $admin_menu->initialize();

            // 缓存数组，不要直接缓存对象。
            $admin_menu = $admin_menu->toArray();
            cache('admin_menu_service::admin_menu', $admin_menu, 3600);
        }

        return $admin_menu;
    }
}

class MenuItem implements \ArrayAccess
{
    /**
     * 根菜单标志。
     */
    const ROOT_NAME = '__ROOT__';

    /**
     * 上级。
     *
     * @var unknown
     */
    protected $_parent;

    /**
     * 名称。
     *
     * @var unknown
     */
    protected $_name;

    /**
     * 数据。
     *
     * @var unknown
     */
    protected $_data = array();

    /**
     * 子项目。
     *
     * @var array
     */
    protected $_children = array();

    /**
     * 构造方法。
     *
     * @param unknown $name
     * @param array $data
     */
    public function __construct($name, $data = array())
    {
        $this->_name = $name;
        $this->_data = $data;
    }

    /**
     * 检查制定key是否存在？
     *
     * @param  [type] $key [description]
     * @return [type]      [description]
     */
    public function offsetExists($offset)
    {
        return isset($this->_data[$offset]);
    }

    /**
     * 设置指定key的值。
     *
     * @param  [type] $offset [description]
     * @param  [type] $value  [description]
     * @return [type]         [description]
     */
    public function offsetSet($offset, $value)
    {
        $this->_data[$offset] = $value;
    }

    /**
     * 获取指定key的值。
     *
     * @param  [type] $offset [description]
     * @return [type]         [description]
     */
    public function offsetGet($offset)
    {
        return $this->_data[$offset];
    }

    /**
     * 删除指定key。
     *
     * @param  [type] $offset [description]
     * @return [type]         [description]
     */
    public function offsetUnset($offset)
    {
        unset($this->_data[$offset]);
    }

    /**
     * 获取ROOT项目。
     *
     * @return unknown
     */
    public function root()
    {
        $parent = $this;
        while ($parent->_parent !== null) {
            $parent = $parent->_parent;
        }
        return $parent;
    }

    /**
     * 获取TOP项目。
     *
     * @return unknown
     */
    public function top()
    {
        $parent = $this;
        while ($parent->_parent != null && $parent->_parent->_name <> self::ROOT_NAME) {
            $parent = $parent->_parent;
        }
        return $parent;
    }

    /**
     * 获取上级项目。
     *
     * @return [type] [description]
     */
    public function parent()
    {
        return $this->_parent;
    }

    /**
     * 获取项目路径。
     *
     * @return [type] [description]
     */
    public function path()
    {
        $list = array();

        $parent = $this;
        while ($parent->_parent !== null) {
            array_unshift($list, $parent->_name);
            $parent = $parent->_parent;
        }

        return implode('/', $list);
    }

    public function fieldList($field)
    {
        $list = array();

        $parent = $this;
        while ($parent->_parent !== null) {
            array_unshift($list, isset($parent[$field]) ? $parent[$field] : null);
            $parent = $parent->_parent;
        }

        return $list;
    }

    /**
     * 获取项目名称。
     *
     * @return [type] [description]
     */
    public function name()
    {
        return $this->_name;
    }

    /**
     * 获取项目值。
     *
     * @return [type] [description]
     */
    public function data()
    {
        return $this->_data;
    }

    /**
     * 获取子项目。
     *
     * @return [type] [description]
     */
    public function children()
    {
        return array_values($this->_children);
    }

    /**
     * 设置指定路径子项目的值。
     *
     * @param [type] $path [description]
     * @param [type] $data [description]
     */
    public function set($path, $data)
    {
        $child = $this->find($path, true);
        $child->_data = (array) $data;
        return $this;
    }

    /**
     * 获取指定路径子项目的值。
     *
     * @param  [type] $path [description]
     * @return [type]       [description]
     */
    public function get($path)
    {
        $child = $this->find($path, false);
        if ($child !== null) {
            return $child->_data;
        } else {
            return null;
        }
    }

    /**
     * 删除指定路径子项目。
     *
     * @param  [type] $path [description]
     * @return [type]       [description]
     */
    public function delete($path)
    {
        $path = trim($path, '/');
        $items = explode('/', $path);
        $last = array_pop(($items));

        if (count($items) > 0) {
            $parent = $this->find(implode('/', $items));
        } else {
            $parent = $this;
        }

        if (isset($this->_children[$last])) {
            unset($this->_children[$last]);
        }
    }

    /**
     * 设置指定路径子项目的值。
     *
     * @param [type] $name [description]
     * @param [type] $data [description]
     */
    public function add($name, $data)
    {
        $child = new MenuItem($name, (array) $data);
        $child->_parent = $this;
        $this->_children[$name] = $child;
        return $child;
    }

    /**
     * 查找指定路径的子项目，路径可以用abc/xyz/rst这种方式表示。
     *
     * @param unknown $path
     * @return NULL|\suifeng\mvc\TreeItem|mixed
     */
    public function find($path, $create = false)
    {
        $path = trim($path, '/');

        $parent =& $this;

        foreach (explode('/', $path) as $child_path) {
            if (isset($parent->_children[$child_path])) {
                $parent =& $parent->_children[$child_path];
            } else {
                //不存在
                if ($create) {
                    $parent =& $parent->add($child_path, []);
                } else {
                    return null;
                }
            }
        }

        return $parent;
    }

    /**
     * 排序菜单项目。
     *
     * @param unknown $key
     * @param unknown $order
     */
    public function sort()
    {
        $temp = array();

        foreach ($this->_children as $name => $child) {
            $temp[$name] = isset($child['sort']) ? $child['sort'] : 100;
        }

        asort($temp);

        foreach (array_keys($temp) as $name) {
            $temp[$name] = $this->_children[$name];
        }

        $this->_children = $temp;

        /*
         uasort($this->_children, function($a, $b) {
         $as = isset($a['sort']) ? $a['sort'] : 100;
         $bs = isset($b['sort']) ? $b['sort'] : 100;
         // 相等的情况下返回1，这样空着$a在前，$b在后。
         return $as == $bs ? 1 : ($as < $bs ? -1 : 1);
         });
         */

        foreach ($this->_children as $child) {
            $child->sort();
        }
    }
}
